package com.tiger.lbs;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.tiger.utils.Http;
import com.tiger.utils.Json;
import com.tiger.utils.Redis;


@Component
public class LBS {
	private final static Logger LOGGER = LoggerFactory.getLogger(LBS.class);
	
	private final static String DIRECTION_URL = "http://api.map.baidu.com/direction/v1?mode=driving&origin={1}&destination={2}&origin_region={3}&destination_region={4}&tactics={5}&output=json&ak={0}";
	private final static String GEOCONV_URL = "http://api.map.baidu.com/geoconv/v1/?coords={1}&to=5&ak={0}";
	private final static String GEOCODER_URL = "http://api.map.baidu.com/geocoder/v2/?ak={0}&coordtype=wgs84ll&location={1}&output=json&pois=0";
	private final static String ROUTE_MATRIX_URL = "http://api.map.baidu.com/direction/v1/routematrix?output=json&origins={1}&destinations={2}&ak={0}&coord_type=bd09ll";
	private final static String PLACE_SUGGESTION="http://api.map.baidu.com/place/v2/suggestion?output=json&ak={0}&query={1}&region={2}";
	private final static String CHARSET="utf-8";
	private String ak;
	
	public static enum TACTICS {
		SHORTER(12),
		FASTER(11);
		
		private final int value;
		
		TACTICS(int value) {
			this.value = value;
		}
		public int getValue() {
			return value;
		}
	}
	
	@Autowired
    public void setAk(@Value("${baidu.ak:}") final String ak) {
        this.ak = ak;
    }
	
	
	public DrivingRoutes drivingRoute(List<Coordinate> points, TACTICS tactics) {
		if(points == null || points.size() < 2) {
			return null;
		}
		if(points.size() == 2) {
			return drivingRoute(points.get(0), points.get(1), tactics);
		}
		DrivingRoutes routes = null;
		for(int i = 1; i < points.size(); i++) {
			DrivingRoutes tmp = drivingRoute(points.get(i - 1), points.get(i), tactics);
			if(tmp == null) {
				return null;
			}
			if(routes == null) {
				routes = tmp;
			} else {
				routes.merge(tmp);
			}
		}
		return routes;
	}
	
	public DrivingRoutes drivingRoute(Coordinate origin, Coordinate destination, TACTICS tactics) {
		try {
			String originStr = origin.getLat() + "," + origin.getLng();
			String destinationStr = destination.getLat() + "," + destination.getLng();
			
			String key = tactics + "_" + originStr + "_" + destinationStr;
			String value = Redis.get(key);
			JsonNode json = null;
			
			if(StringUtils.isEmpty(value)) {
				String url  = MessageFormat.format(DIRECTION_URL, ak, originStr, destinationStr, "", "", tactics.getValue());
				value = Http.get(url);
				if(StringUtils.isEmpty(value)) {
					return null;
				}
				json = Json.toJsonNode(value);
				if(json.get("status").asInt() != 0) {
					return null;
				}
				Redis.setex(key, value, 24);
			} else {
				json = Json.toJsonNode(value);
			}
			
			DrivingRoutes drivingRoutes = Json.fromJson(json.get("result"), DrivingRoutes.class);
			return drivingRoutes;
		} catch (Exception e) {
			return null;
		}
	}
	
	public Coordinate baidu2wgs(Coordinate baiduCoordinate) {
		List<Coordinate> list = baidu2wgs(Arrays.asList(baiduCoordinate));
		return list != null && list.size() > 0 ? list.get(0) : null;
	}
	
	public List<Coordinate> baidu2wgs(List<Coordinate> baiduCoordinateList) {
		if(baiduCoordinateList == null || baiduCoordinateList.size() == 0) {
			return baiduCoordinateList;
		}
		List<Coordinate> list = new ArrayList<Coordinate>();
		StringBuilder sb = new StringBuilder();
		int count = 0;
		for(int n = 0; n < baiduCoordinateList.size(); n++) {
			if(sb.length() != 0) {
				sb.append(';');
			}
			Coordinate coord = baiduCoordinateList.get(n);
			sb.append(coord.getLng()).append(',').append(coord.getLat());
			
			count++;
			if(count == 100 || n == baiduCoordinateList.size() - 1) {
				String result = Http.get(MessageFormat.format(GEOCONV_URL, ak, sb.toString()));
				JsonNode json = Json.toJsonNode(result);
				if(json.get("status").asInt() != 0) {
					return null;
				}
				Iterator<JsonNode> nodes=json.get("result").elements();
				 while (nodes.hasNext()) {
                    JsonNode nod=nodes.next();
                    Coordinate baiduCoordinate = baiduCoordinateList.get(list.size());
					double x = nod.get("x").asDouble();
					double y = nod.get("y").asDouble();
					x = 2 * baiduCoordinate.getLng() - x;
					y = 2 * baiduCoordinate.getLat() - y;
					list.add(new Coordinate(x, y));
				}
				count = 0;
				sb = new StringBuilder();
			}
		}
		return list;
	}
	
	public String queryCityName(Coordinate coord) {
		String result = Http.get(MessageFormat.format(GEOCODER_URL, ak, coord.getLat() + "," + coord.getLng()));
		JsonNode json=Json.toJsonNode(result);
		if(json.get("status").asInt() != 0) {
			return null;
		}
		return json.get("result").get("addressComponent").get("city").asText();
	}
	
	public RouteMatrix queryRouteMatrix(String originStr, String destinationStr) {
		String result = null;
		try {
			result = Http.get(MessageFormat.format(ROUTE_MATRIX_URL, ak, URLEncoder.encode(originStr, CHARSET),  URLEncoder.encode(destinationStr, CHARSET)));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		JsonNode json=Json.toJsonNode(result);
		if(json.get("status").asInt() != 0) {
			return null;
		}
		JsonNode resutlNode = json.get("result").get("elements");
		Iterator<JsonNode> elements = resutlNode.elements();
		DecimalFormat format = new DecimalFormat("#####0.00");
		if (elements.hasNext()) {
			JsonNode nod = elements.next();
			RouteMatrix mat = new RouteMatrix();
			mat.setDistance(nod.get("distance").get("text").asText());
			mat.setDistanceValue(Float.parseFloat(format.format(nod.get("distance").get("value").asDouble()/1000)));
			mat.setTime(nod.get("duration").get("text").asText());
			mat.setTimeValue(Float.parseFloat(format.format(nod.get("duration").get("value").asDouble()/3600)));
			return mat;
		}
		return null;
	}
	
	/**
	 * 获取地理位置查询接口
	 * @param place
	 * @param city
	 * @return
	 */
	public List<Address> queryPlace(String place, String city) {
		String result = null;
		try {
			city = StringUtils.isBlank(city) ? "全国" : city;
			result = Http.get(MessageFormat.format(PLACE_SUGGESTION, ak,URLEncoder.encode(place,CHARSET),URLEncoder.encode(city,CHARSET)));
		} catch (UnsupportedEncodingException e) {
			LOGGER.error("query baidu places error {}", e.getMessage());
			return null;
		}
		JsonNode resultNode= Json.toJsonNode(result).get("result");
		List<Address> address=Json.fromJson(resultNode, new TypeReference<List<Address>>(){});
		if(address == null) {
			return null;
		}
		Iterator<Address> ite = address.iterator();
		while(ite.hasNext()) {
			Address addr = ite.next();
			if(addr.getLocation() == null) {
				ite.remove();
			}
		}
		return address;
	}
}
